package com.bdqn.springsecurity1.config;

import com.bdqn.springsecurity1.filter.ValidCodeFilter;
import com.bdqn.springsecurity1.mapper.MenuMapper;
import com.bdqn.springsecurity1.pojo.Menu;
import com.bdqn.springsecurity1.pojo.Role;
import com.bdqn.springsecurity1.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;


@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    @Autowired
    ValidCodeFilter validCodeFilter;
    @Autowired
    UserService userService;
    @Autowired
    MenuMapper menuMapper;
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
        //如果不想加密就返回
        //return NoOpPasswordEncoder.getInstance();现在版本已经将其移除，淘汰了
    }

    @Bean
    WebSecurityCustomizer webSecurityCustomizer() {
        //忽略这些静态资源（不拦截）
        return (web) -> web.ignoring().requestMatchers("/js/**", "/css/**", "/images/**");
    }

//    @Bean
//    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//        //先验证验证码
//        httpSecurity.addFilterBefore(validCodeFilter, UsernamePasswordAuthenticationFilter.class);
//        //开启登录配置
//        httpSecurity.authorizeHttpRequests()
//                //允许直接访问的路径
//                .requestMatchers("/", "/index", "/validcode").permitAll().anyRequest().authenticated();
//        //开启表单验证
//        httpSecurity.formLogin()
//                //跳转到自定义登录页面
//                .loginPage("/toLogin")
//                //自定义表单的用户名的name属性，默认为username
//                .usernameParameter("username")
//                //自定义表单的密码的name属性，默认为password
//                .passwordParameter("password")
//                //表单请求的地址，使用Security定义好的/login,并且与自定义表单的action一致
//                .loginProcessingUrl("/login")
//                //如果登录失败跳转到
//                .failureUrl("/toLogin/error")
//                //允许访问登录有关的路径
//                .permitAll();
//        //开启注销
//        //注销后跳转到index页面
//        httpSecurity.logout().logoutSuccessUrl("/index");
//        //关闭csrf
//        httpSecurity.csrf().disable();
//        //记住我
//        httpSecurity.exceptionHandling().accessDeniedPage("/errorRole");
//        httpSecurity.rememberMe();
//        httpSecurity.userDetailsService(userService);
//        return httpSecurity.build();
//    }


    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.addFilterBefore(validCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.authorizeHttpRequests(
                register ->
                        register.requestMatchers("/", "/index", "/validcode")
                        .permitAll()
                        .anyRequest().access((authentication, object) -> {
                    //表示请求的 URL 地址和数据库的地址是否匹配上了
                    boolean isMatch = false;
                    //获取当前请求的 URL 地址
                    String requestURI = object.getRequest().getRequestURI();
                    List<Menu> menus = menuMapper.getAllMenus();
                    for (Menu m : menus) {
                        if (antPathMatcher.match(m.getUrl(), requestURI)) {
                            isMatch = true;
                            //说明找到了请求的地址了
                            //这就是当前请求需要的角色
                            List<Role> roles = m.getRoles();
                            //获取当前登录用户的角色
                            Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                            for (GrantedAuthority authority : authorities) {
                                for (Role role : roles) {
                                    if (authority.getAuthority().equals(role.getName())) {
                                        //说明当前登录用户具备当前请求所需要的角色
                                        return new AuthorizationDecision(true);
                                    }
                                }
                            }
                        }
                    }
                    if (!isMatch) {
                        //说明请求的 URL 地址和数据库的地址没有匹配上，对于这种请求，统一只要登录就能访问
                        if (authentication.get() instanceof AnonymousAuthenticationToken) {
                            return new AuthorizationDecision(false);
                        } else {
                            //说明用户已经认证了
                            return new AuthorizationDecision(true);
                        }
                    }
                    return new AuthorizationDecision(false);
                }))
                .formLogin(form ->
                        form.loginPage("/toLogin")
                                .usernameParameter("username")
                                .passwordParameter("password")
                                .loginProcessingUrl("/login")
                                .failureUrl("/toLogin/error")
                                .permitAll()
                )
                .csrf(csrf ->
                        csrf.disable()
                )
                .exceptionHandling(e ->
                        e.accessDeniedPage("/errorRole")
                )
                .logout(logout ->
                        logout.logoutSuccessUrl("/index")
                );
        return http.build();
    }
}
